using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Threading;
using Microsoft.Practices.Unity;
using MiniStock.Entities.Common.Events;
using MiniStock.Infrastructure.Queue;

namespace MiniStock.Infrastructure
{
    public abstract class QueuedEventWorkerBase : WorkerBase
    {
        [Dependency("InternalQueue")]
        public IEventQueue InputEventQueue { get; set; }

        [Dependency]
        public IEventQueue OutputEventQueue { get; set; }

        private ReaderWriterLock EntityIdListLock = new ReaderWriterLock();
        private List<String> EntityIdList = new List<string>();
        
        public void AddEvent(EventBase item)
        {
            try
            {
                EntityIdListLock.AcquireWriterLock(100);
                
                InputEventQueue.Enqueue(item);
                if(item is EntityEventBase) 
                    EntityIdList.Add(((EntityEventBase)item).GetEntityHash());
            } finally
            {
                EntityIdListLock.ReleaseLock();
            }
        }

        public List<string> GetEntityIdList()
        {
            try
            {
                EntityIdListLock.AcquireReaderLock(100);
                return new List<string>(EntityIdList);
            } finally
            {
                EntityIdListLock.ReleaseLock();
            }
        }

        public int GetQueueSize()
        {
            try
            {
                EntityIdListLock.AcquireReaderLock(100);
                return EntityIdList.Count();
            }
            finally
            {
                EntityIdListLock.ReleaseLock();
            }
        }

        protected void PopEventId(EventBase message)
        {
            if (message is EntityEventBase)
                try
                {
                    EntityIdListLock.AcquireReaderLock(1000);
                    EntityIdList.Remove(((EntityEventBase)message).GetEntityHash());
                }
                finally
                {
                    EntityIdListLock.ReleaseLock();
                }
        }

        public override void InitWork()
        {
        }

        public override bool DoWork()
        {
            var message = InputEventQueue.Dequeue();
            if (message == null) return false;

            try
            {
                Trace.CorrelationManager.ActivityId = message.CorrelatedEventId ?? message.EventId;
                Trace.TraceInformation("{0} Processing message: {1} (id={2}, entity id={3})", 
                    this.Name,
                    message.GetType().FullName,
                                       message.EventId,
                                       (message is EntityEventBase ? ((EntityEventBase) message).GetEntityHash() : ""));

                Debug.Assert(!(message is ProtocolEventBase), "Processing protocol events is not supported");

                ProcessMessage((EntityEventBase) message);
                PerformanceMonitoring.IncrementCounter("Dispatch success", this.Name);
                PerformanceMonitoring.IncrementCounter("Dispatch success per second", this.Name);
            } catch(Exception)
            {
                PerformanceMonitoring.IncrementCounter("Dispatch faulted", this.Name);
                PerformanceMonitoring.IncrementCounter("Dispatch faulted per second", this.Name);
                throw;
            } 
            finally
            {
                PopEventId(message);
                Trace.CorrelationManager.ActivityId = Guid.Empty;
            }
            return true;
        }

        public abstract void ProcessMessage(EntityEventBase message);

        public override void CleanupWork()
        {
        }
    }
}